﻿/*
 * G722ToG711Encoder.cpp
 *
 *  Created on: 2017年5月6日
 *      Author: chuanjiang.zh
 */

#include "G722ToG711Encoder.h"
#include "CLog.h"


namespace av
{

G722ToG711Encoder::G722ToG711Encoder():
		m_srcFormat(),
		m_outFormat(),
		m_context(),
		m_swrContext(),
		m_resample()
{
}

G722ToG711Encoder::~G722ToG711Encoder()
{
	close();
}

bool G722ToG711Encoder::open(const AudioFormat& srcFormat, const AudioFormat& outFormat)
{
	if (srcFormat.m_codec != MEDIA_CODEC_G722)
	{
		return false;
	}

	m_srcFormat = srcFormat;
	m_srcFormat.m_channels = 1;
	m_srcFormat.m_samplerate = 16000;

	m_outFormat = outFormat;

	if (!m_decoder.open())
	{
		return false;
	}

	int rc = openEncoder(m_outFormat);
	if (rc != 0)
	{
		close();
		return false;
	}

	int err = 0;
	m_resample = speex_resampler_init(m_outFormat.m_channels, m_srcFormat.m_samplerate, m_outFormat.m_samplerate, 10, &err);

	return true;
}

void G722ToG711Encoder::close()
{
	if (m_swrContext)
	{
		swr_close(m_swrContext);
		swr_free(&m_swrContext);
		m_swrContext = NULL;
	}

	m_decoder.close();

	closeEncoder();

	if (m_resample)
	{
		speex_resampler_destroy(m_resample);
		m_resample = NULL;
	}
}

bool G722ToG711Encoder::isOpen()
{
	return m_decoder.isOpen() && isEncoderOpen();
}

bool G722ToG711Encoder::transcode(AVPacket* inPkt, AVPacket* outPkt)
{
	m_buffer.ensure(inPkt->size * 16 + HKG722Decoder::PACKET_SIZE + 2);

	int size = m_decoder.decode(inPkt->data, inPkt->size, m_buffer.data());
	if (size <= 0)
	{
		return false;
	}

	AVFramePtr frame(av_frame_alloc(), ffmpeg::AVFrameDeleter());
	frame->format = AV_SAMPLE_FMT_S16;
	frame->channels = m_srcFormat.m_channels;
	frame->channel_layout = av_get_default_channel_layout(frame->channels);
	frame->sample_rate = m_srcFormat.m_samplerate;
	frame->pts = inPkt->pts;

	int bytes = av_get_bytes_per_sample((AVSampleFormat)frame->format);
	frame->nb_samples = size / bytes;

	av_frame_get_buffer(frame.get(), 1);

	memcpy(frame->data[0], m_buffer.data(), size);

	int rc = 0;
	if (isSameFormat(frame->format, frame->channels, frame->sample_rate))
	{
		rc = doEncode(frame.get(), outPkt);
	}
	else
	{
        AVFramePtr outFrame(av_frame_alloc(), ffmpeg::AVFrameDeleter());

		outFrame->format = m_context->sample_fmt;
		outFrame->channels = m_context->channels;
		outFrame->channel_layout = m_context->channel_layout;
		outFrame->sample_rate = m_context->sample_rate;
		outFrame->pts = frame->pts;

		//int out_samples = av_rescale_rnd(frame->nb_samples, outFrame->sample_rate, frame->sample_rate, AV_ROUND_UP);
		int out_samples = frame->nb_samples;
        outFrame->nb_samples = out_samples;
        av_frame_get_buffer(outFrame.get(), 1);

		int samples = resample(frame.get(), outFrame.get());
		if (samples > 0)
		{
			rc = doEncode(outFrame.get(), outPkt);
		}
	}

	return rc == 0;
}

AudioFormat G722ToG711Encoder::getOutFormat()
{
	return m_outFormat;
}


int G722ToG711Encoder::resample(AVFrame* inFrame, AVFrame* outFrame)
{
	spx_uint32_t in_len = inFrame->nb_samples;
	spx_uint32_t samples = outFrame->nb_samples;
	int rc = speex_resampler_process_int(m_resample, 0, (spx_int16_t*)inFrame->data[0], &in_len,
		(spx_int16_t*)outFrame->data[0], &samples);

	outFrame->nb_samples = samples;
	outFrame->linesize[0] = outFrame->nb_samples * av_get_bytes_per_sample((AVSampleFormat)outFrame->format);
	return samples;

	/*
    int rc = 0;
    if (m_swrContext == NULL)
    {
        m_swrContext = swr_alloc();

        rc = swr_config_frame(m_swrContext, outFrame, inFrame);

        rc = swr_init(m_swrContext);
        if (rc != 0)
        {
            CLog::error("G722ToG711Encoder. swr_init failed. rc:%d\n", rc);
        }

        //rc = swr_is_initialized(m_swrContext);
    }
    else
    {
        int ret = swr_config_frame(m_swrContext, outFrame, inFrame);
    }

    int ret = swr_convert_frame(m_swrContext, outFrame, inFrame);
	if (ret == 0)
	{
		outFrame->linesize[0] = outFrame->nb_samples * av_get_bytes_per_sample((AVSampleFormat)outFrame->format);
	}
    return outFrame->nb_samples;
	*/
}


int G722ToG711Encoder::openEncoder(AudioFormat& fmt)
{
	AVCodecID codecID = (AVCodecID)fmt.m_codec;
	AVCodec* codec = avcodec_find_encoder(codecID);
	if (!codec)
	{
		return ENOENT;
	}

	m_context = avcodec_alloc_context3(codec);
	if (!m_context)
	{
		return ENOENT;
	}

    if (fmt.m_codec == AV_CODEC_ID_AAC)
    {
	    m_context->profile = FF_PROFILE_AAC_LOW; //profile.m_audioProfile;
    	//m_context->level = profile.m_audioLevel;
    }

	m_context->channels = fmt.m_channels;
    m_context->channel_layout = av_get_default_channel_layout(fmt.m_channels);
	m_context->sample_rate = fmt.m_samplerate;
    m_context->sample_fmt = codec->sample_fmts[0];
    m_context->time_base = av_make_q(1, fmt.m_samplerate);

    m_context->flags |= CODEC_FLAG2_LOCAL_HEADER;

    AVDictionary* options = NULL;
    if (fmt.m_codec == AV_CODEC_ID_AAC)
    {
        av_dict_set(&options, "strict", "-2", 0);
    }

    int rc = avcodec_open2(m_context, codec, &options);

    av_dict_free(&options);

    if (rc < 0)
    {
        char buf[256];
        av_strerror(rc, buf, 256);

        av_free(m_context);
        m_context = NULL;

        return ENOENT;
    }

    if (m_context->extradata_size > 0)
    {
    	fmt.m_config.assign((char*)m_context->extradata, m_context->extradata_size);
    }

	return 0;
}

void G722ToG711Encoder::closeEncoder()
{
	if (m_swrContext)
	{
		swr_close(m_swrContext);
		swr_free(&m_swrContext);
		m_swrContext = NULL;
	}

	if (m_context)
	{
		avcodec_close(m_context);
		av_free(m_context);
		m_context = NULL;
	}
}

bool G722ToG711Encoder::isEncoderOpen()
{
	return (m_context != NULL);
}


bool G722ToG711Encoder::isSameFormat(int fmt, int channels, int sampleRate)
{
	return (m_outFormat.m_channels == channels) &&
        (m_outFormat.m_samplerate == sampleRate) &&
        (fmt == m_context->sample_fmt);
}

int G722ToG711Encoder::doEncode(AVFrame* frame, AVPacket* pkt)
{
	int got_packet = 0;
	int rc = avcodec_encode_audio2(m_context, pkt, frame, &got_packet);
	if (rc == 0)
	{
		rc = got_packet ? 0 : EAGAIN;
	}

	return rc;
}



} /* namespace av */
